﻿using log4net;
using log4net.Appender;
using log4net.Config;
using log4net.Repository.Hierarchy;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Configuration;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Web;
using TechTree.Stalk.Roots.Exceptions;

namespace TechTree.Stalk.Roots.Logging
{
    public enum LogType
    {
        RollingFile,
        Smtp
    }

    public enum Severity
    {
        Debug,
        Error,
        Fatal,
        Information,
        Warning
    }

    public enum Category
    {
        Execution,
        Performance,
        Statistic
    }

    public class Log
    {
        #region Constants
        private const string DEFAULT_LOG_TITLE = "ERROR";
        private const string LOG_CONFIGURATION_FILE_KEY = "log4net-config-file";
        private const string MESSAGE_ERROR_NULL_OR_EMPTY_APPENDER_NAME = "Unable to get log appender by name. The logger name is null or empty.";
        #endregion

        #region Constructors
        static Log()
        {
            XmlConfigurator.Configure(new FileInfo(ConfigurationManager.AppSettings[LOG_CONFIGURATION_FILE_KEY]));
        }
        #endregion

        #region Attributes
        private static Dictionary<String, ILog> _loggers = new Dictionary<String, ILog>();
        #endregion

        #region Public Methods
        public static void Write(Exception exception_)
        {
            Write(DEFAULT_LOG_TITLE, String.Empty, Category.Execution, Severity.Error, exception_, LogType.RollingFile);
        }
        public static void Write(Exception exception_, LogType logType_)
        {
            Write(DEFAULT_LOG_TITLE, String.Empty, Category.Execution, Severity.Error, exception_, logType_);
        }
        public static void Write(string title_, string message_, Category category_, Severity severity_)
        {
            Write(title_, message_, category_, severity_, null, LogType.RollingFile);
        }
        public static void Write(string title_, string message_, Category category_, Severity severity_, LogType logType_)
        {
            Write(title_, message_, category_, severity_, null, logType_);
        }
        public static void Write(string title_, string message_, Category category_, Severity severity_, Exception exception_)
        {
            Write(title_, message_, category_, severity_, exception_, LogType.RollingFile);
        }
        public static void Write(string title_, string message_, Category category_, Severity severity_, Exception exception_, LogType logType_)
        {
            ILog logger = GetLogger(logType_);

            switch (severity_)
            {
                case (Severity.Debug):
                    {
                        logger.Debug(message_, exception_);
                        break;
                    }
                case (Severity.Error):
                    {
                        logger.Error(message_, exception_);
                        break;
                    }
                case (Severity.Fatal):
                    {
                        logger.Fatal(message_, exception_);
                        break;
                    }
                case (Severity.Information):
                    {
                        logger.Info(message_, exception_);
                        break;
                    }
                case (Severity.Warning):
                    {
                        logger.Warn(message_, exception_);
                        break;
                    }
            }
        }
        #endregion

        #region Private Methods
        private static ILog GetLogger(LogType logType_)
        {
            return GetLogger(logType_.ToString());
        }
        private static ILog GetLogger(string loggerName_)
        {
            if (!String.IsNullOrEmpty(loggerName_))
            {
                lock (_loggers)
                {
                    if (!_loggers.ContainsKey(loggerName_))
                    {

                        ILog log = LogManager.GetLogger(loggerName_);
                        _loggers.Add(loggerName_, log);
                        return log;
                    }

                    return _loggers[loggerName_];
                }
            }
            throw new LoggingException(MESSAGE_ERROR_NULL_OR_EMPTY_APPENDER_NAME);
        }
        #endregion
    }
}
